package com.example.demo.task.service;

import com.example.demo.annotation.DistributedLock;
import com.example.demo.common.SpringContextUtil;
import com.example.demo.enums.StatusEnum;
import com.example.demo.task.DynamicExecteTask;
import com.example.demo.task.ScheduledStatus;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @Auther Created by l.wang on 2019/1/18.
 */
public class TaskSchedulerService {
    private final Log logger = LogFactory.getLog(getClass());
    private ConcurrentMap<String, ScheduledFuture<?>> map = new ConcurrentHashMap<>();
    private ThreadPoolTaskScheduler scheduler;
    private Lock lock  = new ReentrantLock();
    public void setScheduler(ThreadPoolTaskScheduler scheduler) {
        this.scheduler = scheduler;
    }

    /**
     * 调用此方法做以校正
     * {"123":{"cancelled":true,"done":true},"1234":{"cancelled":false,"done":false}}
     * 123为key，cancelled 与 done 都等于true，此方法己停止。相反1234 为key，cancelled 与 done 都等 false为启动
     * 获取运行中任务Key的列表
     * @return
     */
    private ConcurrentMap<String, ScheduledFuture<?>> getAllExecuteClazzId(){
        return map;
    }

    /**
     * 校验
     * @param statuses
     * @return
     */
    public List<ScheduledStatus> correction(List<ScheduledStatus> statuses){
        ConcurrentMap<String, ScheduledFuture<?>> stringScheduledFutureConcurrentMap = getAllExecuteClazzId();
        final List<ScheduledStatus> scheduledStatuses = new ArrayList<>();
        statuses.stream().forEach(run -> {
            final ScheduledStatus status = new ScheduledStatus();
            ScheduledFuture<?> future = stringScheduledFutureConcurrentMap.get(run.getKey());
            if(future.isCancelled() && run.getStatus().equals(StatusEnum.START)){//任务己停止有两种可能
//                if(run.getStatus().equals(StatusEnum.START)){
                //2、己完成
                if (future.isDone()){
                }
                //1、未完成
                if(!future.isDone()){
                    future.cancel(true);
                }
                status.setKey(run.getKey());
                status.setStatus(StatusEnum.STOP);
            }else if(!future.isCancelled() && run.getStatus().equals(StatusEnum.STOP)){//任务己启动有两种可能
                //2、己完成
                if (future.isDone()){
//                    objectMap.put(run.getKey(), StatusEnum.STOP);
                }
                //1、未完成
                if(!future.isDone()){
                    future.cancel(true);
//                    objectMap.put(run.getKey(), StatusEnum.STOP);
                }
                status.setKey(run.getKey());
                status.setStatus(StatusEnum.STOP);
            }
            if(!StringUtils.isEmpty(status)){
                scheduledStatuses.add(status);
            }
        });

        return scheduledStatuses;
    }

    public ScheduledStatus correction(String keys,StatusEnum status){
        ScheduledStatus scheduledStatus = new ScheduledStatus();
        scheduledStatus.setKey(keys);
        scheduledStatus.setStatus(status);
        List<ScheduledStatus> scheduledStatuses = correction(Arrays.asList(scheduledStatus));
        if(scheduledStatuses.isEmpty()){
            return null;
        }
        Optional<ScheduledStatus> opt = correction(Arrays.asList(scheduledStatus)).stream().findFirst();
        return opt.get();
    }


    /**
     * 启动
     * @param key 执行主建
     * @param executeTime 执行时间
     * @param clazz 执行线程
     * @return
     */
    private StatusEnum runTaskScheduler(String key, String executeTime, Runnable clazz) {
        ScheduledFuture<?> future = map.get(key);//获取执行方法
        if (!StringUtils.isEmpty(future)){
            if(!future.isCancelled()){
                logger.info("task running");
                return StatusEnum.RUNNING;//"任务正在执行，请先停止任务";
            }
        }
        future = scheduler.schedule(clazz, new CronTrigger(executeTime));
        map.put(key, future);
        logger.info("startCron():"+future);
        return StatusEnum.START;//"任务启动成功";
    }

    /**
     * 停止
     * @param key 执行主建
     * @return
     */
    public StatusEnum stopTaskScheduler(String key) {
        ScheduledFuture<?> future = map.get(key);
        if (future != null) {
            if(future.isCancelled()){
                logger.info("It has stopped running");
                return StatusEnum.STOPPED;//"任务己经停止";
            }
            future.cancel(true);
            logger.info("stopCron():" + future);
            return StatusEnum.STOP;//"任务停止成功";
        }else{
            return StatusEnum.NULL;
        }
    }

    /**
     * 删除己停止任务
     * @param key
     */
    public void delTaskScheduler(String key){
        ScheduledFuture<?> future = map.get(key);
        if (!StringUtils.isEmpty(future)){
            if(!future.isCancelled()){
                logger.info("task running,not delete");
            }else{
                map.remove(key);
            }
        }
    }

    public StatusEnum startTaskScheduler(String key, String executeTime,String clazz,String method){
        return startTaskScheduler(key,executeTime ,clazz ,method , null);
    }

    /**
     * 执行定时任务
     * @param key 执行主建 ；需要唯一性
     * @param executeTime 执行时间 (格式：0 0 5 ? * 4 * )
     * @param clazz 类名
     * @param method 方法名
     * @return
     */
    @DistributedLock("'schedu' + #key")
    public StatusEnum startTaskScheduler(String key, String executeTime,String clazz,String method,Object... parameter){
        lock.lock();
        try {
            DynamicExecteTask execteTask = new DynamicExecteTask();
            Object obj = SpringContextUtil.getBean(clazz); //获取己注入类
            Method[] methods = obj.getClass().getMethods(); //获取类所有方法
            /**查找与需要执行的方法**/
            Optional<Method> opt = Arrays.asList(methods).stream().filter(md->md.getName().equals(method)).findFirst();
            if (!opt.isPresent()){
                logger.info("not find method");
                return StatusEnum.METHOD;
            }
            execteTask.setClazz(obj);//需要执行任务类
            execteTask.setExecute(opt.get());//需要执行任务方法
            execteTask.setParameter(parameter);
            return runTaskScheduler(key,executeTime,execteTask);//执行
        }catch (BeansException e) {
            logger.info(clazz, e);
            return StatusEnum.CLAZZ;
        }catch (SecurityException e){
            logger.info(method, e);
            return StatusEnum.METHOD;
        }finally {
            lock.unlock();
        }
    }


}
